﻿/*
 * Managed ESL Suite
 * 
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 * 
 * The Initial Developer of the Original Code is
 * Diego Toro <dftoro@yahoo.com>
 * Portions created by the Initial Developer are Copyright (C)
 * the Initial Developer. All Rights Reserved.
 * 
 * Contributor: 
 * 
 */

using System;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Managed.Esl.Suite
{
  public delegate void ESLConnectionSession(object Sender, ESLSession session);

  public class EventManager
  {
    #region Field

    private string stHost;
    private int inPort;
    private string stPassword;
    private string stSubscribeEvent;
    private EESLMode enEslMode;
    private bool boIsRunning;

    private TcpListener tcListener;
    private ManualResetEvent mrResetEvent;

    #endregion Field

    #region Property

    public string Host
    {
      get { return stHost; }
    }

    public int Port
    {
      get { return inPort; }
    }

    public string Password
    {
      get { return stPassword; }
    }

    public string SubscribedEvent
    {
      get { return stSubscribeEvent; }
      set { stSubscribeEvent = value; }
    }

    public EESLMode EslMode
    {
      get { return enEslMode; }
    }

    #endregion Property

    #region Constructor

    public EventManager()
    {
      stHost = null;
      inPort = -1;
      stPassword = null;
      enEslMode = EESLMode.Inbound;
      boIsRunning = false;
    }

    public EventManager(string host, int port, string password, EESLMode eslMode)
      : this()
    {
      stHost = host;
      inPort = port;
      stPassword = password;
      enEslMode = eslMode;

      if (String.IsNullOrEmpty(stSubscribeEvent))
      {
        stSubscribeEvent = "event plain ALL";
      }
    }

    public EventManager(string host, int port, string password, string subscribeEvent)
      : this(host, port, password, EESLMode.Inbound)
    {
      stSubscribeEvent = subscribeEvent;
    }

    #endregion Constructor

    #region Event

    public event ESLConnectionSession OnESLSession;

    #endregion Event

    #region Public Member

    public void Start()
    {
      ESLSession esSession;
      Configuration coFile = null;

      #region Try get configuration from config file

      if (String.IsNullOrEmpty(stHost))
      {
        ExeConfigurationFileMap exFileMap = new ExeConfigurationFileMap();
        exFileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "FreeSwitch.Managed.Esl.dll.config");
        coFile = ConfigurationManager.OpenMappedExeConfiguration(exFileMap, ConfigurationUserLevel.None);

        if (coFile == null)
        {
          throw new Exception("Error loading config file");
        }

        if (!coFile.AppSettings.Settings.AllKeys.Contains("Host") || !coFile.AppSettings.Settings.AllKeys.Contains("Port"))
        {
          throw new Exception("Error, Host and Port aren't defined in config file");
        }

        stHost = coFile.AppSettings.Settings["Host"].Value;
        inPort = int.Parse(coFile.AppSettings.Settings["Port"].Value);
      }

      if (coFile != null && String.IsNullOrEmpty(stSubscribeEvent))
      {
        //load from config file
        if (!coFile.AppSettings.Settings.AllKeys.Contains("SubscribeEvent"))
        {
          throw new Exception("Error no SubscribeEvent defined in config file");
        }

        stSubscribeEvent = coFile.AppSettings.Settings["SubscribeEvent"].Value;
      }

      //try get eslmode from config file
      if (coFile != null)
      {
        if (coFile.AppSettings.Settings.AllKeys.Contains("ESLMode") && coFile.AppSettings.Settings["ESLMode"] != null
          && !String.IsNullOrEmpty(coFile.AppSettings.Settings["ESLMode"].Value))
        {
          enEslMode = (EESLMode)Enum.Parse(typeof(EESLMode), coFile.AppSettings.Settings["ESLMode"].Value, true);
        }
      }


      #endregion Try get configuration from config file

      switch (enEslMode)
      {
        case EESLMode.Inbound:

          #region Inbound

          #region Try get password from config file

          if (String.IsNullOrEmpty(stPassword))
          {
            if (coFile == null)
            {
              throw new Exception("Error loading config file");
            }

            if (!coFile.AppSettings.Settings.AllKeys.Contains("Password") || String.IsNullOrEmpty(coFile.AppSettings.Settings["Password"].Value))
            {
              throw new Exception("Error, Password isn't defined in config file");
            }

            stPassword = coFile.AppSettings.Settings["Password"].Value;
          }

          #endregion Try get password from config file

          mrResetEvent = new ManualResetEvent(false);

          esSession = new ESLSession(stHost, inPort, stPassword, stSubscribeEvent);

          try
          {
            if (OnESLSession != null)
            {
              var OnESLSessionLocal = OnESLSession;
              OnESLSessionLocal(this, esSession);
            }

            mrResetEvent.WaitOne();
          }
          catch (Exception ex)
          {
            throw ex;
          }
          finally
          {
            if (esSession.IsConnected)
            {
              esSession.Disconnect();
            }

            Trace.Flush();
          }


          break;

          #endregion Inbound

        case EESLMode.AsynOutbound:
        case EESLMode.SynOutbound:

          #region AsynOutbound and SynOutbound

          tcListener = new TcpListener(IPAddress.Parse(stHost), inPort);

          try
          {
            tcListener.Start();
            boIsRunning = true;

            while (boIsRunning)
            {
              Trace.WriteLine("Waiting connection...");
              Socket sckClient = tcListener.AcceptSocket();

              ThreadPool.QueueUserWorkItem((o) =>
              {
                Debug.WriteLine("Socket Handler " + sckClient.Handle.ToInt32() + " Th " + Thread.CurrentThread.ManagedThreadId);
                esSession = new ESLSession(sckClient.Handle.ToInt32(), enEslMode);

                try
                {
                  if (OnESLSession != null)
                  {
                    var OnESLSessionLocal = OnESLSession;
                    OnESLSessionLocal(this, esSession);
                  }
                }
                catch (Exception ex)
                {
                  throw ex;
                }
                finally
                {
                  if (esSession.IsConnected)
                  {
                    esSession.Disconnect();
                  }

                  Debug.Flush();
                }
              });
            }
          }
          catch (Exception ex)
          {
            throw ex;
          }
          finally
          {
            tcListener.Stop();
          }

          break;

          #endregion AsynOutbound and SynOutbound
      }
    }

    public void Stop()
    {
      boIsRunning = false;

      if (mrResetEvent != null)
      {
        mrResetEvent.Set();
      }
    }

    #endregion Public Member

  }
}
